package chapter_12;

// 输入/删除、小应用程序和其他主题

/*
两个重要的包：io和applet。io包支持Java的基本I/O（输入/输出）系
统，包括文件的输入/输出。applet包支持applet（小应用程序）

12.1 输入/输出基础
    12.1.1 流的概念
    Java程序通过流来完成输入/输出。流是生产或消费信息的抽象。流通过Java的输入/输
    出系统与物理设备链接。

    12.1.2 字节流和字符流
    Java 2 定义了两种类型的流：字节类和字符类。字节流（byte stream）为处理字节的
    输入和输出提供了方便的方法。例如使用字节流读取或书写二进制数据。字符流（character
    stream）为字符的输入和输出处理提供了方便。它们采用了统一的编码标准，因而可以国
    际化。当然，在某些场合，字符流比字节流更有效。
    需要声明：在最底层，所有的输入/输出都是字节形式的。基于字符的流只为处理字符
    提供方便有效的方法。

    字节流类
    字节流由两个类层次结构定义。在顶层有两个抽象类：InputStream 和 OutputStream。
    每个抽象类都有多个具体的子类，这些子类对不同的外设进行处理，例如磁盘文件，网络
    连接，甚至是内存缓冲区。
    BufferedInputStream 缓冲输入流
    BufferedOutputStream 缓冲输出流
    ByteArrayInputStream 从字节数组读取的输入流
    ByteArrayOutputStream 向字节数组写入的输出流
    DataInputStream 包含读取Java标准数据类型方法的输入流
    DataOutputStream 包含编写Java 标准数据类型方法的输出流
    FileInputStream 读取文件的输入流
    FileOutputStream 写文件的输出流
    FilterInputStream 实现 InputStream
    FilterOutputStream 实现 OutputStream
    InputStream 描述流输入的抽象类
    OutputStream 描述流输出的抽象类
    PipedInputStream 输入管道
    PipedOutputStream 输出管道
    PrintStream 包含print( ) 和 println( )的输出流
    PushbackInputStream 支持向输入流返回一个字节的单字节的“unget”的输入流
    RandomAccessFile 支持随机文件输入/输出
    SequenceInputStream 两个或两个以上顺序读取的输入流组成的输入流
    抽象类InputStream 和 OutputStream定义了实现其他流类的关键方法。最重要的两种方
    法是read()和write()，它们分别对数据的字节进行读写。两种方法都在InputStream 和
    OutputStream中被定义为抽象方法。它们被派生的流类重载。

    字符流类
    字符流类由两个类层次结构定义。顶层有两个抽象类：Reader和Writer。这些抽象类处
    理统一编码的字符流。Java中这些类含有多个具体的子类。
    BufferedReader 缓冲输入字符流
    BufferedWriter 缓冲输出字符流
    CharArrayReader 从字符数组读取数据的输入流
    CharArrayWriter 向字符数组写数据的输出流
    FileReader 读取文件的输入流
    FileWriter 写文件的输出流
    FilterReader 过滤读
    FilterWriter 过滤写
    InputStreamReader 把字节转换成字符的输入流
    LineNumberReader 计算行数的输入流
    OutputStreamWriter 把字符转换成字节的输出流
    PipedReader 输入管道
    PipedWriter 输出管道
    PrintWriter 包含print( )和println( )的输出流
    PushbackReader 允许字符返回到输入流的输入流
    Reader 描述字符流输入的抽象类
    StringReader 读取字符串的输入流
    StringWriter 写字符串的输出流
    Writer 描述字符流输出的抽象类
    抽象类Reader和Writer定义了几个实现其他流类的关键方法。其中两个最重要的是read()
    和write()，它们分别进行字符数据的读和写。这些方法被派生流类重载。

    12.1.3 预定义流
    所有的Java程序自动导入java.lang包。该包定义了一个名为System的类，该类封装了运
    行时环境的多个方面。例如，使用它的某些方法，你能获得当前时间和与系统有关的不同
    属性。System 同时包含三个预定义的流变量，in，out和err。这些成员在System中是被定义
    成public 和static型的，这意味着它们可以不引用特定的System对象而被用于程序的其他部
    分。
    System.out是标准的输出流。默认情况下，它是一个控制台。System.in是标准输入，默
    认情况下，它指的是键盘。System.err指的是标准错误流，它默认是控制台。然而，这些流
    可以重定向到任何兼容的输入/输出设备。
    System.in 是inputStream的对象；System.out和System.err是PrintStream的对象。它们都
    是字节流，尽管它们用来读写外设的字符。如果愿意，你可以用基于字符的流来包装它们。
 */


/*
12.2 读取控制台输入
Java中，控制台输入由从System.in读取数据来完成。为获得属于控制台的字符流，在
BufferedReader对象中包装System.in。BufferedReader 支持缓冲输入流。它最常见的构造函
数如下：
BufferedReader(Reader inputReader)
这里，inputReader是链接被创建的BufferedReader实例的流。Reader是一个抽象类。它
的一个具体的子类是InputStreamReader，该子类把字节转换成字符。为获得链接System.in
的一个InputStreamReader的对象，用下面的构造函数：
InputStreamReader(InputStream inputStream)
因为System .in引用了InputStream 类型的对象，它可以用于inputStream。综上所述，下
面的一行代码创建了与键盘相连的BufferedReader对象。
BufferedReader br = new BufferedReader(new
InputStreamReader(System.in));
当该语句执行后，br是通过System.in生成的链接控制台的字符流。

    12.2.1 读取字符
    从BufferedReader读取字符，用read()。我们所用的read()版本如下：
    int read( ) throws IOException
    该方法每次执行都从输入流读取一个字符然后以整型返回。当遇到流的末尾时它返回
    -1。你可以看到，它要引发一个IOException异常。

    12.2.2 读取字符串
    从键盘读取字符串，使用readLine()。它是BufferedReader类的成员。它的通常形式如下：
    String readLine() throws IOException
 */


/*
12.3 向控制台写输出
控制台输出由前面描述过的print( ) 和 println( )来完成最为简单，它们被用在本书的大
多数例题中。这两种方法由PrintStream(System.out引用的对象类型)定义。尽管System.out
是一个字节流，用它作为简单程序的输出是可行的。字符流输出在下节介绍。
因为PrintStream是从OutputStream派生的输出流，它同样实现低级方法write( )，write( )
可用来向控制台写数据。PrintStream 定义的write( )的最简单的形式如下：
void write(int byteval)
该方法按照byteval指定的数向文件写字节。尽管byteval 定义成整数，但只有低位的8
个字节被写入。
 */


/*
12.4 PrintWriter类
尽管Java允许用System.out向控制台写数据，但建议仅用在调试程序时或在例题中，对于实际
的程序，Java推荐的向控制台写数据的方法是用PrintWriter流。PrintWriter是基于字符的类。
用基于字符类向控制台写数据使程序更为国际化。
PrintWriter定义了多个构造函数，我们所用到的一个如下：
PrintWriter(OutputStream outputStream, boolean flushOnNewline)
这里，outputStream是OutputStream类的对象，flushOnNewline控制Java是否在println( )
方法被调用时刷新输出流。如果flushOnNewline为true，刷新自动发生，若为false，则不发
生。
PrintWriter支持所有类型（包括Object）的print( )和println( )方法，这样，你就可以像用
System.out那样用这些方法。如果遇到不同类型的情况，PrintWriter方法调用对象的toString( )
方法并打印结果。
用PrintWriter向外设写数据，指定输出流为System.out并在每一新行后刷新流。
 */


/*
12.5 文件的读写
Java为你提供了一系列的读写文件的类和方法。在Java中，所有的文件都是字节形式的。
Java提供从文件读写字节的方法。而且，Java允许在字符形式的对象中使用字节文件流。
两个最常用的流类是FileInputStream和FileOutputStream，它们生成与文件链接的字节
流。为打开文件，你只需创建这些类中某一个类的一个对象，在构造函数中以参数形式指
定文件的名称。这两个类都支持其他形式的重载构造函数。下面是我们将要用到的形式：
FileInputStream(String fileName) throws FileNotFoundException
FileOutputStream(String fileName) throws FileNotFoundException
当你对文件的操作结束后，需要调用close( )来关闭文件。该方法在FileInputStream和
FileOutputStream中都有定义。如下：
void close( ) throws IOException
为读文件，可以使用在FileInputStream中定义的read( )方法。我们用到的如下：
int read( ) throws IOException
该方法每次被调用，它仅从文件中读取一个字节并将该字节以整数形式返回。当读到
文件尾时，read( )返回-1。该方法可以引发IOException异常。

向文件中写数据，需用FileOutputStream定义的write()方法。它的最简单形式如下：
void write(int byteval) throws IOException
该方法按照byteval指定的数向文件写入字节。尽管byteval作为整数声明，但仅低8位字
节可以写入文件。如果在写的过程中出现问题，一个IOException被引发。
 */


/*
12.6 小应用程序基础
小应用程序（applet）是访问internet服务器，在internet上传播的，自动安装的，作为
部分Web文档运行的小应用程序。当小应用程序到达客户端，它被限制访问资源，以使它能够
在不受病毒威胁和破坏数据完整性的情况下生成一个二进制的多媒体用户界面以及完成复杂
的计算。
注意小应用程序没有main()方法，不像Java应用程序，小应用程序不以main()为程序起
始。实际上，大多数小应用程序甚至不含main()方法。相反，当小应用程序类名被传输到
小应用程序阅读器(applet view)或网络浏览器时它开始执行。
 */


/*
12.7 transient和volatile修饰符
transient是类型修饰符，只能用来修饰字段。在对象序列化的过程中，标记为transient
的变量不会被序列化。
示例：
class Test {
    transient int a; // 不会被持久化
    int b; // 持久化
}
当类Test的实例对象被序列化（比如将Test类的实例对象 t 写入硬盘的文本文件t.txt中），
变量 a 的内容不会被保存，变量 b 的内容则会被保存。

volatile
volatile 也是变量修饰符，只能用来修饰变量。volatile修饰的成员变量在每次被线程访
问时，都强迫从共享内存中重读该成员变量的值。而且，当成员变量发生变 化时，强迫线程
将变化值回写到共享内存。这样在任何时刻，两个不同的线程总是看到某个成员变量的同一个
值。
在此解释一下Java的内存机制：
Java使用一个主内存来保存变量当前值，而每个线程则有其独立的工作内存。线程访问变量的
时候会将变量的值拷贝到自己的工作内存中，这样，当线程对自己工作内存中的变量进行操作
之后，就造成了工作内存中的变量拷贝的值与主内存中的变量值不同。
Java语言规范中指出：为了获得最佳速度，允许线程保存共享成员变量的私有拷贝，而且只当
线程进入或者离开同步代码块时才与共享成员变量的原始值对比。
这样当多个线程同时与某个对象交互时，就必须要注意到要让线程及时的得到共享成员变量的变化。
而volatile关键字就是提示VM：对于这个成员变量不能保存它的私有拷贝，而应直接与共享
成员变量交互。
使用建议：在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在
synchronized代码块中，或者为常量时，不必使用。
由于使用volatile屏蔽掉了VM中必要的代码优化，所以在效率上比较低，因此一定在必要时才
使用此关键字。
 */


/*
12.8 使用instanceof
instanceof运算符具有下面的一般形式：
object instanceof type
这里，object是类的实例，而type是类的类型。如果object是指定的类型或者可以被强制
转换成指定类型，instanceof将它评估成true，若不是，则结果为false。
 */


/*
12.9 strictfp
strictfp 关键字可应用于类、接口或方法。使用 strictfp 关键字声明一个方法时，该方法
中所有的float和double表达式都严格遵守FP-strict的限制,符合IEEE-754规范。当对一个
类或接口使用 strictfp 关键字时，该类中的所有代码，包括嵌套类型中的初始设定值和代码，
都将严格地进行计算。严格约束意味着所有表达式的结果都必须是 IEEE 754 算法对操作数预
期的结果，以单精度和双精度格式表示。
如果你想让你的浮点运算更加精确，而且不会因为不同的硬件平台所执行的结果不一致的话，
可以用关键字strictfp.
 */


/*
12.10 本机方法
Java提供了native关键字，该关键字用来声明本机代码方法。一旦声明，这些方法可以
在Java程序中被调用，就像调用其他Java方法一样。
为声明一个本机方法，在该方法之前用native修饰符，但是不要定义任何方法体。例如：
public native int meth() ;
声明本机方法后，必须编写本机方法并要执行一系列复杂的步骤使它与Java代码链接。
 */


import java.io.*;
import java.awt.*;
import java.applet.*;


class BRRead {
    static void runner() throws IOException {
        char c;
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("Enter characters, 'q' to quit.");

        // read characters
        do {
            c = (char) br.read();
            System.out.println(c);
        } while (c != 'q');
    }
}


class BRReadLiens {
    // 12.2.2
    static void runner() throws IOException {
        // create a BufferedReader using System.in
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str;
        System.out.println("Enter lines of text.");
        System.out.println("Enter 'stop' to quit");
        do {
            str = br.readLine();
            System.out.println(str);
        } while (!str.equals("stop"));
    }
}


class WriteDemo {
    //12.3
    static void runner() {
        int b;
        b = 'A';
        System.out.write(b);
        System.out.write('\n');
    }
}


class PrintWriteDemo {
    // 12.4
    static void runner() {
        PrintWriter pw = new PrintWriter(System.out, true);
        pw.println("This is a string");
        int i = -7;
        pw.println(i);
        double d = 4.5e-7;
        pw.println(d);
    }
}


class ShowFile {
    // 12.5 读取文件
    static void runner() throws IOException {
        int i;
        FileInputStream fin;

        try {
            fin = new FileInputStream("./resource/a.txt");
        } catch (FileNotFoundException e) {
            System.out.println("File Not Found");
            return;
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Usage: ShowFile File");
            return;
        }

        // read characters until EOF is encountered
        do {
            i = fin.read();
            if (i != -1)
                System.out.print((char) i);
        } while (i != -1);
        fin.close();
    }
}


class CopyFile {
    // 12.5 写入文件
    static void runner() throws IOException
    {
        int i;
        FileInputStream fin;
        FileOutputStream fout;
        try {
            // open input file
            try {
                fin = new FileInputStream("./resource/a.txt");
            } catch(FileNotFoundException e) {
                System.out.println("Input File Not Found");
                return;
            }
            // open output file
            try {
                fout = new FileOutputStream("./resource/b.txt");
            } catch(FileNotFoundException e) {
                System.out.println("Error Opening Output File");
                return;
            }
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("Usage: CopyFile From To");
            return;
        }
        // Copy File
        try {
            do {
                i = fin.read();
                if(i != -1)
                    fout.write(i);
            } while(i != -1);
        } catch(IOException e) {
            System.out.println("File Error");
        }
        fin.close();
        fout.close();
    }
}

// 12.8
class A {
    int i, j;
}
class B {
    int i, j;
}
class C extends A {
    int k;
}
class D extends A {
    int k;
}
class InstanceOf {
    static void runner() {
        A a = new A();
        B b = new B();
        C c = new C();
        D d = new D();
        if(a instanceof A)
            System.out.println("a is instance of A");
        if(b instanceof B)
            System.out.println("b is instance of B");
        if(c instanceof C)
            System.out.println("c is instance of C");
        if(c instanceof A)
            System.out.println("c can be cast to A");
        if(a instanceof C)
            System.out.println("a can be cast to C");
        System.out.println();
        // compare types of derived types
        A ob;
        ob = d; // A reference to d
        System.out.println("ob now refers to d");
        if(ob instanceof D)
            System.out.println("ob is instance of D");
        System.out.println();
        ob = c; // A reference to c
        System.out.println("ob now refers to c");
        if(ob instanceof D)
            System.out.println("ob can be cast to D");
        else
            System.out.println("ob cannot be cast to D");
        if(ob instanceof A)
            System.out.println("ob can be cast to A");
        System.out.println();
        // all objects can be cast to Object
        if(a instanceof Object)
            System.out.println("a may be cast to Object");
        if(b instanceof Object)
            System.out.println("b may be cast to Object");
        if(c instanceof Object)
            System.out.println("c may be cast to Object");
        if(d instanceof Object)
            System.out.println("d may be cast to Object");
        /*
        多数程序不需要instanceof运算符，因为，一般来说，你知道你正在使用的对象类型。
        但是，在你编写对复杂类层次结构对象进行操作的通用程序时它是非常有用的。
         */
    }
}


public class chapter_12 {

    public static void main(String[] args) {
        // 12.2.1
//        try {
////            BRRead.runner();
//            BRReadLiens.runner();
//        } catch (IOException e) {
//            System.out.println("Some error.");
//        }

        // 12.3
//        WriteDemo.runner();

        // 12.4
//        PrintWriteDemo.runner();

        // 12.5
//        try {
////            ShowFile.runner();
//            CopyFile.runner();
//        } catch (IOException e) {
//            System.out.println("发生了错误。");
//        }

        // 12.8
        InstanceOf.runner();
    }
}
